﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

using NUnit.Framework;

using Boggle;

namespace Boggle.Tests
{
    class TestWord
	{
		#region Ctor

		[Test, TestCaseSource("GetCtorCases")]
        public void TestCtor(String wordText, IList<Die> dice)
        {
            var word = new Word(wordText, dice);
            Assert.AreEqual(wordText, word.WordText);
            CollectionAssert.AreEqual(dice, word.Dice);
            Assert.IsNull(word.Definition);
            Assert.AreEqual(0, word.Score);
            Assert.AreEqual(Word.Status.Validating, word.CurrentStatus);
        }

        private IEnumerable<ITestCaseData> GetCtorCases()
        {
            yield return new TestCaseData(
                null,
                new List<Die> { })
                .Throws<ArgumentNullException>();

            yield return new TestCaseData(
                "a",
                null);

            yield return new TestCaseData(
                "a",
                new List<Die> { new Die("b") })
                .Throws<ArgumentException>();

            yield return new TestCaseData(
                "a",
                new List<Die> { new Die("a") });

            yield return new TestCaseData(
                "alex",
                new List<Die> { new Die("a"), new Die("l"), new Die("e"), new Die("x") });

            yield return new TestCaseData(
                "alex",
                new List<Die> { new Die("a"), new Die("l"), new Die("x"), new Die("e") })
                .Throws<ArgumentException>();
		}

		#endregion

		#region Score

		/// <summary>
		/// Tests the Score property.
		/// </summary>
		[Test, TestCaseSource("GetScoreCases")]
		public Int32 TestScore(String w, Word.Status status)
		{
			var word = new Word(w, w.Select(c => new Die(c.ToString())).ToList())
			{
				CurrentStatus = status
			};

			return word.Score;
		}

		/// <summary>
		/// Provides test cases for the Score test.
		/// </summary>
		/// <returns>Test cases for the Score test method.</returns>
		public IEnumerable<ITestCaseData> GetScoreCases()
		{
			yield return new TestCaseData("", Word.Status.Validated).Returns(0);
			yield return new TestCaseData("a", Word.Status.Validated).Returns(0);
			yield return new TestCaseData("ab", Word.Status.Validated).Returns(0);
			yield return new TestCaseData("abc", Word.Status.Validated).Returns(1);
			yield return new TestCaseData("abcd", Word.Status.Validated).Returns(1);
			yield return new TestCaseData("abcde", Word.Status.Validated).Returns(2);
			yield return new TestCaseData("abcdef", Word.Status.Validated).Returns(3);
			yield return new TestCaseData("abcdefg", Word.Status.Validated).Returns(5);
			yield return new TestCaseData("abcdefgh", Word.Status.Validated).Returns(11);

			yield return new TestCaseData("", Word.Status.Validating).Returns(0);
			yield return new TestCaseData("", Word.Status.NotInGrid).Returns(0);
			yield return new TestCaseData("", Word.Status.NotAWord).Returns(0);
			yield return new TestCaseData("", Word.Status.Error).Returns(0);

			yield return new TestCaseData("abc", Word.Status.Validating).Returns(0);
			yield return new TestCaseData("abc", Word.Status.NotInGrid).Returns(0);
			yield return new TestCaseData("abc", Word.Status.NotAWord).Returns(0);
			yield return new TestCaseData("abc", Word.Status.Error).Returns(0);

			yield return new TestCaseData("abcdefgh", Word.Status.Validating).Returns(0);
			yield return new TestCaseData("abcdefgh", Word.Status.NotInGrid).Returns(0);
			yield return new TestCaseData("abcdefgh", Word.Status.NotAWord).Returns(0);
			yield return new TestCaseData("abcdefgh", Word.Status.Error).Returns(0);
		}

		#endregion

		#region PropertyChanged

		/// <summary>
		/// Tests the PropertyChanged method.
		/// </summary>
		[Test, TestCaseSource("GetPropertyChangedCases")]
		public void TestPropertyChanged(String w, Action<Word> op, List<String> expectedPropertyChanges)
		{
			var actualPropertyChanges = new List<String>();
			var word = new Word(w, w.Select(c => new Die(c.ToString())).ToList());
			PropertyChangedEventHandler handler = (o, e) => actualPropertyChanges.Add(e.PropertyName);
			word.PropertyChanged += handler;
			op(word);
			word.PropertyChanged -= handler;
			CollectionAssert.AreEqual(expectedPropertyChanges, actualPropertyChanges);
		}

		/// <summary>
		/// Provides test cases for the PropertyChanged test.
		/// </summary>
		/// <returns>Test cases for the PropertyChanged test method.</returns>
		public IEnumerable<ITestCaseData> GetPropertyChangedCases()
		{
			yield return new TestCaseData("hello", new Action<Word>(w => w.CurrentStatus = Word.Status.NotInGrid), new List<String> { "CurrentStatus" })
				.SetName("When CurrentStatus changes to NotInGrid, we get CurrentStatus change notification");

			yield return new TestCaseData("hello", new Action<Word>(w => w.CurrentStatus = Word.Status.Validated), new List<String> { "CurrentStatus", "Score" })
				.SetName("When CurrentStatus changes to Validated, we get CurrentStatus and Score change notification");

			yield return new TestCaseData("hello", new Action<Word>(w => w.Definition = "Def"), new List<String> { "Definition" })
				.SetName("When Definition changes, we get Definition change notification");
		}

		#endregion
	}
}
